import { window, ExtensionContext, QuickInputButton, Uri, QuickPickItem, QuickInput, QuickInputButtons, CancellationToken, Disposable } from 'vscode';
import { logger } from './logger';
import * as vscode from 'vscode';
import { spawn } from 'child_process';

export async function componentCompile(context: ExtensionContext, args: any[]) {

	const title = 'Component Compile';

	const state = await collectInputs(title);
    
	if (args.length === 0) {
        vscode.window.showErrorMessage('No args provided, please see the log for more information');
        logger.errorWithStack('No args provided');
        return;
    }

    const uri = args[0] as vscode.Uri;
    if (!uri || typeof uri.fsPath !== 'string') {
        vscode.window.showErrorMessage('Invalid URI provided, , please see the log for more information');
        logger.errorWithStack('Invalid URI provided');
        return;
    }

    let path = uri.fsPath;
    
    // Check if path is a file, if not, append bundle.json to it
    const fs = require('fs');
    if (fs.existsSync(path) && fs.statSync(path).isDirectory()) {
        path = `${path}/bundle.json`;
    }

    logger.info(`bundle.json's path is : ${path}`);
    const pathParts = path.split(/[/\\]/);
    const parentFolderName = pathParts.length >= 2 ? pathParts.slice(-2, -1)[0] : '';

    if (!parentFolderName) {
        vscode.window.showErrorMessage('Could not extract parent folder name from path, please see the log for more information');
        logger.errorWithStack('Could not extract parent folder name from path');
        return;
    }
    logger.info(`parentFolderName is : ${parentFolderName}`);


    const cancellationTokenSource = new vscode.CancellationTokenSource();
    
    vscode.window.withProgress({
        location: vscode.ProgressLocation.Notification,
        title: `[${parentFolderName}]: Begin to Compile...`,
        cancellable: true
    },
    async (progress, token) => {
        // Execute ls command
        const dirPath = path.substring(0, path.lastIndexOf('/'));
        
        // Find the root directory containing .repo folder
        let repoRootPath = dirPath;
        let foundRepoDir = false;
        
        try {
            while (repoRootPath && repoRootPath !== '/') {
                const potentialRepoDir = `${repoRootPath}/.repo`;
                const fs = require('fs');
                
                if (fs.existsSync(potentialRepoDir) && fs.statSync(potentialRepoDir).isDirectory()) {
                    foundRepoDir = true;
                    logger.info(`Found .repo directory at: ${repoRootPath}`);
                    break;
                }
                
                // Move up one directory
                repoRootPath = repoRootPath.substring(0, repoRootPath.lastIndexOf('/'));
            }
            
            if (!foundRepoDir) {
                logger.error('Could not find .repo directory in any parent folder');
                vscode.window.showErrorMessage(`[${parentFolderName}]: Could not find .repo directory`);
                return false;
            }
        } catch (error) {
            logger.errorWithStack('Error while searching for .repo directory');
            vscode.window.showErrorMessage(`[${parentFolderName}]: Error while searching for .repo directory`);
            return false;
        }
        
        try {
            // 执行命令
            const result = await executeAndLogCommand(state, dirPath, parentFolderName, token, repoRootPath);

            if (result && result.status === 'success') {
                // 成功时的操作
                vscode.window.showInformationMessage(`[${parentFolderName}]: Compile Success`);
                return true;
            } else {
                vscode.window.showErrorMessage(`[${parentFolderName}]: Compile failed, ${result?.message}`);
                return false;
            }
        } catch (error) {
            // 发生错误时显示错误消息
            if (error instanceof Error) {
                logger.errorWithStack(error.message);
            } else {
                logger.errorWithStack("Compile failed");
            }
            vscode.window.showErrorMessage(`[${parentFolderName}]: Compile failed, Please check the log for more information`);     
            return false; // 表示操作失败
        }
    });
}

async function executeAndLogCommand(state: State, dirPath: string, parentFolderName: string, token?: vscode.CancellationToken, repoRootPath?: string) {
    try {
        logger.show();
        logger.info(`Begin to compile, dirPath is ${dirPath}, parentFolderName is ${parentFolderName}`);
        
        let commandStr = `hb build ${parentFolderName}`;
        if (state.compileSourceCode) {
            commandStr += ' -i';
        }
        if (state.compileTestCode) {
            commandStr += ' -t';
        }

        // commandStr = '../test.sh';

        // 获取命令执行的工作目录（文件所在目录）
        const workingDir = repoRootPath;
        
        // 将命令字符串分解为命令和参数
        const parts = commandStr.split(' ');
        const command = parts[0];
        const args = parts.slice(1);
        
        const process = spawn(command, args, { 
            cwd: workingDir 
        });

        // 实时处理标准输出
        process.stdout.on('data', (data) => {
            const output = data.toString().trim();
            if (output) {
                output.split('\n').forEach((line: string) => {
                    if (line.trim()) {
                        logger.hb(`  ${line}`);
                    }
                });
            }
        });
        
        // 实时处理标准错误
        process.stderr.on('data', (data) => {
            const output = data.toString().trim();
            if (output) {
                output.split('\n').forEach((line: string) => {
                    if (line.trim()) {
                        logger.hb(`  ${line}`);
                    }
                });
            }
        });
        
        return new Promise<{ status: string; message?: string }>((resolve, reject) => {
            // 保存对取消状态的引用
            let isCancelled = false;
            
            if (token) {
                token.onCancellationRequested(() => {
                    isCancelled = true;
                    process.kill();
                    logger.info(`Compile cancelled by user`);
                    resolve({ status: 'failed', message: 'Compile cancelled by user' });
                });
            }
            
            process.on('close', (code) => {
                // 如果已经因为取消而解析了，就不要重复解析
                if (isCancelled) {
                    return;
                }
                
                if (code === 0) {
                    resolve({ status: 'success' });
                } else {
                    const errorMsg = `命令 ${commandStr} 执行失败，退出码: ${code}`;
                    logger.errorWithStack(errorMsg);
                    reject({ status: 'failed', message: "please check the log for more information" });
                }
            });
            
            process.on('error', (err) => {
                // 如果已经因为取消而解析了，就不要重复解析
                if (isCancelled) {
                    return;
                }
                
                logger.errorWithStack(`command execution error: ${err.message}`);
                reject({ status: 'failed', message: "please check the log for more information" });
            });
        });
    } catch (error) {
        if (error instanceof Error) {
            logger.errorWithStack(`执行命令时出错: ${error.message}`);
        } else {
            logger.errorWithStack(`执行命令时发生未知错误`);
        }
        return { status: 'failed', message: 'please check the log for more information' };
    }
}

interface State {
    title: string;
    step: number;
    totalSteps: number;
    compileSourceCode: boolean;
    compileTestCode: boolean;
    buildFormFactor: "RK3568",
}

async function collectInputs(title: string) {
    const state = {} as Partial<State>;
    const compileTypeItems: QuickPickItem[] = ['源码', '测试用例'].map(label => ({ label }));
    await MultiStepInput.run(input => pickCompileType(compileTypeItems, title, input, state));
    return state as State;
}

async function pickCompileType(
    items: QuickPickItem[],
    title: string, input: MultiStepInput, state: Partial<State>) {
    const pick = await input.showQuickPick(true, {
        title,
        step: 1,
        totalSteps: 2,
        placeholder: 'Pick a compile type',
        items: items,
        activeItem: undefined,
        buttons: [],
        shouldResume: () => Promise.resolve(false)
    });

    const selectedItems = pick.map(item => item.label);
    if (selectedItems.length === 0) {
        logger.errorWithStack('No compile type selected');
        state.compileSourceCode = undefined;
        state.compileTestCode = undefined;
    } else {
        // Update state based on selections
        state.compileSourceCode = selectedItems.includes('源码');
        state.compileTestCode = selectedItems.includes('测试用例');
    }

    // Move to the next step if needed
    const factorItems: QuickPickItem[] = ['RK3568'].map(label => ({ label }));
    return (input: MultiStepInput) => pickBuildFormFactor( factorItems, 'Pick a build form factor', input, state);
}


async function pickBuildFormFactor(
    items: QuickPickItem[],
    title: string, input: MultiStepInput, state: Partial<State>) {

    // 仅展示
    await input.showQuickPick(false, {
        title,
        step: 2,
        totalSteps: 2,
        placeholder: 'Pick a build form factor',
        items: items,
        activeItem: undefined,
        buttons: [],
        shouldResume: () => Promise.resolve(false)
    });
}


type InputStep = (input: MultiStepInput) => Thenable<InputStep | void>;

class InputFlowAction {
	static back = new InputFlowAction();
	static cancel = new InputFlowAction();
	static resume = new InputFlowAction();
}

interface QuickPickParameters<T extends QuickPickItem> {
	title: string;
	step: number;
	totalSteps: number;
	items: T[];
	activeItem?: T;
	ignoreFocusOut?: boolean;
	placeholder: string;
	buttons?: QuickInputButton[];
	shouldResume: () => Thenable<boolean>;
}

class MultiStepInput {

	static async run(start: InputStep) {
		const input = new MultiStepInput();
		return input.stepThrough(start);
	}

	private current?: QuickInput;
	private steps: InputStep[] = [];

	private async stepThrough(start: InputStep) {
		let step: InputStep | void = start;
		while (step) {
			this.steps.push(step);
			// dispose是会有延迟的
			if (this.current) {
				this.current.enabled = false;
				this.current.busy = true;
			}
			try {
				step = await step(this);
			} catch (err) {
				if (err === InputFlowAction.back) {
					this.steps.pop();
					step = this.steps.pop();
				} else if (err === InputFlowAction.resume) {
					step = this.steps.pop();
				} else if (err === InputFlowAction.cancel) {
					step = undefined;
				} else {
					throw err;
				}
			}
		}
		if (this.current) {
			this.current.dispose();
		}
	}

	async showQuickPick<T extends QuickPickItem, P extends QuickPickParameters<T>>(canSelectMany: boolean, {title, step, totalSteps, items, activeItem, ignoreFocusOut, placeholder, buttons, shouldResume }: P) {
		const disposables: Disposable[] = [];
		try {
			return await new Promise<T[] | (P extends { buttons: (infer I)[] } ? I : never)>((resolve, reject) => {
				const input = window.createQuickPick<T>();
				input.canSelectMany = canSelectMany;
				input.title = title;
				input.step = step;
				input.totalSteps = totalSteps;
				input.ignoreFocusOut = ignoreFocusOut ?? false;
				input.placeholder = placeholder;
				input.items = items;
			
				if (activeItem) {
					input.activeItems = [activeItem];
				}
				input.buttons = [
					...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
					...(buttons || [])
				];

				disposables.push(
					input.onDidChangeSelection(items => {
						if (items.length > 0) {
							resolve(Array.from(items));
						} else {
							// 如果没有选择项，更新 placeholder 提示用户
							window.showInformationMessage('Please select at least one item');
						}
					})
				);

				if (this.current) {
					this.current.dispose();
				}
				this.current = input;
				this.current.show();
			});
		} finally {
			disposables.forEach(d => d.dispose());
		}
	}
}